package server;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;

import model.Message;
import model.MessageType;
import server.exception.ClientDoesNotExistException;

public class ServerMain
{
	protected static final int PORT = 1025;
	
	protected static ServerView serverView;
	
	private static ConcurrentHashMap<String, Client> clients;
	private static ConcurrentHashMap<String, Client> negotiationClients;
	protected static DispatcherThread dispatcher;
	
	private static LinkedBlockingQueue<Message> messageQueue = new LinkedBlockingQueue<Message>();

	public static void main(String[] args)
	{
		serverView = new ServerView();
		
		dispatcher = new DispatcherThread();
		dispatcher.start();
		
		clients = new ConcurrentHashMap<String, Client>();
		negotiationClients = new ConcurrentHashMap<String, Client>();
		
		final int port = serverView.getPort();
		try
		{
			WelcomeClientThread welcomeClient = new WelcomeClientThread(port);
			welcomeClient.start();
		}
		catch (IOException e)
		{
			e.printStackTrace();
			return;
		}
		
		while (true) {
			try
			{
				Message message = messageQueue.take();
			
				switch (message.getType())
				{
					case VERIFY_LOGIN:
						final String clientGeneratedId = message.getFrom();
						final String clientId = message.getText();
						if (isLoginAccepted(clientId))
						{
							// caso o login do usuário em negociação seja aceito,
							// atualiza como cliente permanente;
							// envia mensagem de "login aceito"
							// envia mensgem com a lista de usuários logados
							Client client = removeNegotiationClient(clientGeneratedId);
							client.setClientId(clientId);
							addClient(client);
							generateAndSendLoginAcceptedMessage(clientId);
							generateAndBroadcastListOnlineClients();
						}
						else
						{
							// caso o login do usuário em negociação não seja aceito,
							// envia a mensagem de "login recusado"
							generateAndSendLoginRefusedMessage(clientGeneratedId);
						}
						break;
					case LOGOUT_REQUEST:
						generateAndSendLogoutAckMessage(message.getFrom());
					default:
						break;
				}
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}
	}

	private static boolean isLoginAccepted(String text)
	{
		return !clients.keySet().contains(text);
	}
	
	protected static void addNegotiationClient(final Client client)
	{
		negotiationClients.put(client.getClientId(), client);
	}
	
	private static Client removeNegotiationClient(final String clientId)
	{
		return negotiationClients.remove(clientId);
	}
	
	protected static Client getNegotiationClient(final String clientId) throws ClientDoesNotExistException
	{
		final Client client = negotiationClients.get(clientId);
		
		if (client == null) {
			throw new ClientDoesNotExistException();
		}
		
		return client;
	}

	protected static void addClient(final Client client)
	{
		clients.put(client.getClientId(), client);
	}
	
	protected static Client getClient(final String clientId) throws ClientDoesNotExistException
	{
		final Client client = clients.get(clientId);
		
		if (client == null) {
			throw new ClientDoesNotExistException();
		}
		
		return client;
	}
	
	protected static void removeClient(final Client client)
	{
		ServerMain.serverView.printLog("Client suddenly closed.");
		clients.remove(client.getClientId());
		negotiationClients.remove(client.getClientId());
		generateAndBroadcastListOnlineClients();
	}
	
	protected static void addMessage(final Message message)
	{
		try
		{
			messageQueue.put(message);
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}
	}
	
	private static void generateAndBroadcastListOnlineClients()
	{
		Set<String> onlineClients = clients.keySet();
		
		for (String client : onlineClients)
		{
			Message message = new Message(MessageType.LIST_ONLINE_CLIENTS, onlineClients.toString(), client);
			dispatcher.addMessage(message);
		}
	}
	
	private static void generateAndSendLoginAcceptedMessage(final String clientId)
	{
		dispatcher.addMessage(new Message(MessageType.LOGIN_ACCEPTED, "", clientId));
	}
	
	private static void generateAndSendLoginRefusedMessage(final String clientId)
	{
		dispatcher.addMessage(new Message(MessageType.LOGIN_REFUSED, "", clientId));
	}
	
	private static void generateAndSendLogoutAckMessage(final String clientId)
	{
		dispatcher.addMessage(new Message(MessageType.LOGOUT_ACK, "", clientId));
	}
}
